package timing.ukulele.storage.service;

import io.minio.Result;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import timing.ukulele.common.data.ResponseCode;
import timing.ukulele.common.data.ResponseData;
import timing.ukulele.storage.dictionary.RoleTypeEnum;
import timing.ukulele.storage.entity.UserEntity;
import timing.ukulele.storage.repository.UserRepository;
import timing.ukulele.storage.service.minio.BucketService;
import timing.ukulele.storage.util.UpdateUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@Slf4j
public class UserService {
    private final UserRepository repository;
    private final MongoOperations mongoOperations;
    private final BucketService bucketService;
    private final PasswordEncoder passwordEncoder;

    public UserService(UserRepository userRepository, MongoOperations mongoOperations, BucketService bucketService, PasswordEncoder passwordEncoder) {
        this.repository = userRepository;
        this.mongoOperations = mongoOperations;
        this.bucketService = bucketService;
        this.passwordEncoder = passwordEncoder;
    }

    public boolean existed(UserEntity filter) {
        Optional<UserEntity> one = repository.findOne(Example.of(filter));
        return one.isPresent();
    }

    /**
     * 增加一个用户即增加一个桶
     *
     * @param user
     * @return
     */
    public UserEntity addUser(UserEntity user) {
        // 默认启用
        user.setActive(Boolean.TRUE);
        // 默认普通用户角色
        if (!StringUtils.hasLength(user.getRole()))
            user.setRole(RoleTypeEnum.USER.name());
        user.setPassword(passwordEncoder.encode("123456"));
        UserEntity save = repository.save(user);
        try {
            // 使用账户作为存储桶
            if (!bucketService.bucketExists(user.getAccount()))
                bucketService.makeBucket(user.getAccount());
            return save;
        } catch (Exception e) {
            e.printStackTrace();
            repository.delete(save);
            log.error("创建用户的存储桶失败！");
            return null;
        }
    }

    public UserEntity updateUser(UserEntity user) throws IllegalAccessException {
        mongoOperations.upsert(new Query(Criteria.where("_id").is(user.getId())), UpdateUtil.getUpdateValues(user), user.getClass());
        Optional<UserEntity> byId = repository.findById(user.getId());
        return byId.orElse(null);
    }

    /**
     * 如果用户的存储桶下面还有文件则不能删除
     *
     * @param id
     * @return
     */
    public ResponseData<Boolean> deleteUser(String id) {
        ResponseData<Boolean> response = new ResponseData<>();
        Optional<UserEntity> entity = repository.findById(id);
        if (!entity.isPresent()) {
            response.setMessage("用户不存在");
            response.setData(Boolean.FALSE);
            response.setCode(ResponseCode.BUSINESS_ERROR.getCode());
            return response;
        }
        try {
            Iterable<Result<Item>> results = bucketService.listObjects(entity.get().getAccount(), null, false, false);
            if (results != null && results.iterator().hasNext()) {
                response.setMessage("请先处理该用户的文件");
                response.setData(Boolean.FALSE);
                response.setCode(ResponseCode.BUSINESS_ERROR.getCode());
                return response;
            }
            bucketService.removeBucket(entity.get().getAccount());
            repository.deleteById(id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        response.setMessage("成功");
        response.setData(Boolean.TRUE);
        response.setCode(ResponseCode.SUCCESS.getCode());
        return response;
    }

    public UserEntity findById(String id) {
        Optional<UserEntity> byId = repository.findById(id);
        return byId.orElse(null);
    }

    public UserEntity findByAccount(String account) {
        UserEntity filter = new UserEntity();
        filter.setAccount(account);
        filter.setActive(Boolean.TRUE);
        filter.setRole(null);
        Optional<UserEntity> optional = repository.findOne(Example.of(filter));
        if (!optional.isPresent())
            return null;
        return optional.get();
    }

    public ResponseData<String> reset(String id) throws IllegalAccessException {
        ResponseData<String> response = new ResponseData<>();
        UserEntity byId = this.findById(id);
        if (byId == null) {
            response.setMessage("用户不存在");
            response.setData("用户不存在");
            response.setCode(ResponseCode.BUSINESS_ERROR.getCode());
            return response;
        }
        UserEntity user = new UserEntity();
        user.setId(id);
        user.setPassword(passwordEncoder.encode("123456"));
        user.setRole(null);
        this.updateUser(user);
        response.setMessage("重置成功");
        response.setData("重置成功");
        response.setCode(ResponseCode.SUCCESS.getCode());
        return response;
    }

    public ResponseData<String> change(String username, String oldPwd, String newPwd) throws IllegalAccessException {
        ResponseData<String> response = new ResponseData<>();
        UserEntity entity = this.findByAccount(username);
        if (entity == null) {
            response.setMessage("用户不存在");
            response.setData("用户不存在");
            response.setCode(ResponseCode.BUSINESS_ERROR.getCode());
            return response;
        }
        if (passwordEncoder.matches(oldPwd, entity.getPassword())) {
            UserEntity user = new UserEntity();
            user.setId(entity.getId());
            user.setPassword(passwordEncoder.encode(newPwd));
            user.setRole(null);
            this.updateUser(user);
            response.setMessage("重置成功");
            response.setData("重置成功");
            response.setCode(ResponseCode.SUCCESS.getCode());
            return response;
        } else {
            response.setMessage("旧密码错误");
            response.setData("旧密码错误");
            response.setCode(ResponseCode.BUSINESS_ERROR.getCode());
            return response;
        }
    }

    public Page<UserEntity> getUserPage(int current, int size, String groupId, Sort sort) {
        if (StringUtils.hasLength(groupId)) {
            List<UserEntity> userEntities = filterByGroup(groupId);
            return CollectionUtils.isEmpty(userEntities) ? new PageImpl<>(new ArrayList<>(0), PageRequest.of(current, size, sort), 0)
                    : new PageImpl<>(userEntities, PageRequest.of(current, userEntities.size(), sort), userEntities.size());
        } else {
            final Page<UserEntity> all = repository.findAll(PageRequest.of(current, size, sort));
            return all;
        }
    }

    public List<UserEntity> filterByGroup(String groupId) {
        List<AggregationOperation> list = new ArrayList<>();
        list.add(Aggregation.match(Criteria.where("group").elemMatch(Criteria.where("$eq").is(groupId))));
        TypedAggregation<UserEntity> agg = Aggregation.newAggregation(UserEntity.class, list);
        AggregationResults<UserEntity> aggregate = mongoOperations.aggregate(agg, UserEntity.class, UserEntity.class);
        return aggregate.getMappedResults();
    }
}
